#include "edge.h"
#include <QHash>

class EdgePrivate
{
	Q_DISABLE_COPY(EdgePrivate)
public:
	static EdgePrivate *instance(Vertex u, Vertex v, int length = 1);
	static EdgePrivate *instance(const QString &uname, const QString &vname, int length = 1);
	~EdgePrivate();
	Vertex u;
	Vertex v;
	int length;
	QAtomicInt refCounter;
private:
	EdgePrivate(Vertex _u, Vertex _v, int l = 1) : u(_u), v(_v), length(l) { refCounter.ref(); }
	EdgePrivate(const QString &uname, const QString &vname, int l = 1) : u(uname), v(vname), length(l) { refCounter.ref(); }
	static QHash<QPair<QString, QString>, EdgePrivate *> instances;
};

QHash<QPair<QString, QString>, EdgePrivate *> EdgePrivate::instances;

EdgePrivate *EdgePrivate::instance(Vertex u, Vertex v, int length)
{
	EdgePrivate *p = 0;
	if (instances.contains(qMakePair(u.name(), v.name()))) {
		p = instances.value(qMakePair(u.name(), v.name()));
		p->refCounter.ref();
	} else if (instances.contains(qMakePair(v.name(), u.name()))) {
		p = instances.value(qMakePair(v.name(), u.name()));
		p->refCounter.ref();
	} else {
		p = new EdgePrivate(u, v, length);
		instances.insert(qMakePair(u.name(), v.name()), p);
	}
	return p;
}

EdgePrivate *EdgePrivate::instance(const QString &uname, const QString &vname, int length)
{
	EdgePrivate *p = 0;
	if (instances.contains(qMakePair(uname, vname))) {
		p = instances.value(qMakePair(uname, vname));
		p->refCounter.ref();
	} else if (instances.contains(qMakePair(vname, uname))) {
		p = instances.value(qMakePair(vname, uname));
		p->refCounter.ref();
	} else {
		p = new EdgePrivate(uname, vname, length);
		instances.insert(qMakePair(uname, vname), p);
	}
	return p;
}

EdgePrivate::~EdgePrivate()
{
	instances.remove(qMakePair(u.name(), v.name()));
	instances.remove(qMakePair(v.name(), u.name()));
}

Edge::Edge()
	: d(EdgePrivate::instance("", "", 0))
{
}

Edge::Edge(const QString &uname, const QString &vname, int length)
	: d(EdgePrivate::instance(uname, vname, length))
{
}

Edge::Edge(Vertex u, Vertex v, int length)
	: d(EdgePrivate::instance(u, v, length))
{
}

Edge::Edge(const Edge &other)
	: d(other.d)
{
	d->refCounter.ref();
}

bool Edge::operator==(const Edge &other) const
{
	return d == other.d;
}

Edge &Edge::operator=(const Edge &other)
{
	if (!d->refCounter.deref())
		delete d;
	d = other.d;
	d->refCounter.ref();
	return *this;
}

bool Edge::isNull() const
{
	return d->u.isNull() || d->v.isNull() || !(d->length);
}

Vertex Edge::u()
{
	return d->u;
}

const Vertex Edge::u() const
{
	return d->u;
}

Vertex Edge::v()
{
	return d->v;
}

const Vertex Edge::v() const
{
	return d->v;
}

uint Edge::length() const
{
	return d->length;
}

void Edge::setLength(uint length)
{
	d->length = length;
}

uint qHash(const Edge &e)
{
	return qHash(e.u().name()) ^ qHash(e.v().name());
}
